home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Human Interface Toolbox / Concordia / ChooseTkl.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  14.5 KB  |  403 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        ChooseTkl.c
  3.  
  4.     Contains:    just one enourmously long routine, DoChooseMsg.  This
  5.                 routine handles the display of a menu after it's been pulled down or popped up
  6.                 and before it's erased.
  7.  
  8.     Written by:     
  9.  
  10.     Copyright:    Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved.
  11.  
  12.                 You may incorporate this Apple sample source code into your program(s) without
  13.                 restriction. This Apple sample source code has been provided "AS IS" and the
  14.                 responsibility for its operation is yours. You are not permitted to redistribute
  15.                 this Apple sample source code as "Apple sample source code" after having made
  16.                 changes. If you're going to re-distribute the source, we require that you make
  17.                 it clear in the source that the code was descended from Apple sample source
  18.                 code, but that you've made changes.
  19.  
  20.     Change History (most recent first):
  21.                 8/10/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  22.                 
  23.  
  24. */
  25.  
  26. /******************************************************************************\
  27. * Header Files
  28. \******************************************************************************/
  29.  
  30. #include <Memory.h>
  31. #include <Menus.h>
  32. #include <Quickdraw.h>
  33. #include <Types.h>
  34. #include "ChooseTkl.h"
  35. #include "Concordia.h"
  36. #include "DrawTkl.h"
  37. #include "SizeTkl.h"
  38.  
  39.  
  40. /******************************************************************************\
  41. * Constants & Macros
  42. \******************************************************************************/
  43.  
  44. #define noScroll   ((short) 0) //Must not scroll
  45. #define upScroll   ((short) 1) //Must scroll up
  46. #define downScroll ((short) 2) //Must scroll down
  47.  
  48. /* System Globals */
  49. #define sgHuhRect     *((Rect *) 0x09FA)  //MenuSelect seems to need this
  50. #define sgMenuDisable *((long *) 0x0B54)  //MenuDisable system global (IM-V)
  51.  
  52.  
  53. /******************************************************************************\
  54. * Function Prototypes
  55. \******************************************************************************/
  56.  
  57. void RedrawItem (MenuHandle, short, Rect *, short);
  58.  
  59.  
  60. #pragma segment Main
  61. /******************************************************************************\
  62. * DoChooseMsg - Choose a menu item
  63. *
  64. * DoChooseMsg is an unbelievably long routine which processes the mChooseMsg
  65. * menu message for the menu specified by TheMenu.  DoChooseMsg will determine
  66. * which menu items must be hilighted and unhighlighted, and will highlight and
  67. * unhighlight those items appropriately.  If TheMenu has scroll arrows and the
  68. * location of the mouse (given in HitPt), is located in these scroll arrows,
  69. * DoChooseMsg will scroll the menu.  DoChooseMsg will then draw any newly-
  70. * exposed menu items.  When the end of TheMenu is reached while scrolling, the
  71. * scroll arrow at that end will be erased and the menu item behind it will be
  72. * drawn.  If a scrolling menu was at one end when it's scrolled, DoChooseMsg
  73. * will draw a new scroll arrow at that end.  The item number of the chosen item
  74. * is returned, or 0 if no item is chosen.
  75. *
  76. * Coding Notes
  77. * #A# - This loop calculates the item number and item rectangle of the item
  78. *       being chosen, the item rectangle of the item that was chosen the last
  79. *       time DoChooseMsg was called, and the sum of the heights of all of the
  80. *       items in TheMenu.  If no item is chosen or if the item is disabled, the
  81. *       item number will be calculated as 0.
  82. * #B# - Read as: If the vertical coordinate in the TopMenuItem system global
  83. *       doesn't coincide with the top of the menu rectangle, this menu has a top
  84. *       scroll arrow.
  85. * #C# - Read as: If the vertical coordinate in the AtMenuBottom system global
  86. *       doesn't coincide with the bottom of the menu rectangle, this menu has a
  87. *       bottom scroll arrow.
  88. * #D# - If the menu has a top or bottom scroll arrow, set the clipping region so
  89. *       that the scroll arrows won't be stomped on when items are hilighted and
  90. *       unhilighted.
  91. * #E# - If we're scrolling and there's no scroll bar at the other end, it must
  92. *       be drawn and the scroll rectangle adjusted for that.
  93. * #F# - If we've scrolled to an end of a menu, the scroll bar at that end must
  94. *       be drawn over.
  95. * #G# - This loop draws the menu items that intersect with UpdateRect.
  96. * #H# - If we saved the clip region, we'd better restore it.
  97. * #I# - Side Effect: System globals TopMenuItem and AtMenuBottom are modified
  98. *       here.  Icky, Poo!
  99. * #J# - Set high word of MenuDisable system global to menu ID, and low word to
  100. *       the item number.  Yes folks, even if the item is disabled.
  101. * #K# - If the item is disabled, its item number will be the 2's complement of
  102. *       whatever it should be.
  103. * #L# - Why do I have to do this?  BEATS THE HELL OUT OF ME!
  104. * #M# - Even if the new item isn't hierarchical, it might still get stomped on
  105. *       when a hierarchical menu is erased.
  106. * #N# - We don’t need the menuEnabled bit any more so get rid of it by shifting
  107. *       it out.  Then we have to set the high bit so that all items beyond the
  108. *       31st will be enabled.  With arithmetic right shifting, this high bit
  109. *       will be preserved.
  110. * #O# - If a hierarchical menu is up right at the top or bottom of the screen
  111. *       and the user moves the mouse into the scroll area of the main menu, we
  112. *       get a mChooseMsg with the hit point in the scroll area before the child
  113. *       menu is brought down.  That would cause DoChooseMsg to scroll the main
  114. *       menu while the child menu is still up.  Because the Menu Bar Definition
  115. *       Procedure saves the bits behind the child menu and because the child
  116. *       menu slightly overlaps the main menu, scrolling while the hierarchical
  117. *       menu is up causes a little bit of garbage to appear on the screen.  To
  118. *       prevent this, the DoDrawMsg routine clears a field in the MBSaveLoc
  119. *       record to 0.  DoChooseMsg checks this field.  If it’s set to 1, then
  120. *       scrolling is done as usual.  If it’s clear, then scrolling is supressed
  121. *       to give the menu bar definition procedure some time to bring down the
  122. *       hierarchical menu first.
  123. \******************************************************************************/
  124.  
  125. short
  126. DoChooseMsg (MenuHandle TheMenu,Rect* MenuRect,Point HitPt,short WhichItem)
  127. {
  128.     Str255        *ItemString; //-> Menu item's string
  129.     ItemInfoPtr   ItemInfo;    //-> Item info record
  130.     short         ItemHeight;  //Height of menu item in pixels
  131.     Rect          ItemRect;    //Item's rectangle in screen coords
  132.     short         TotHeight;   //Total height of menu in pixels
  133.     Rect          NewRect;     //Rectangle of newly-chosen item in global coords
  134.     short         NewItem;     //Item number of newly-chosen item
  135.     Rect          OldRect;     //Rectangle of old chosen item in global coords
  136.     short         OldItem;     //Item number of old chosen item
  137.     short         CurrItem;    //Item number of item being processed
  138.     short         NewIsHier;   //True if NewItem specifies hierarchical item
  139.     short         OldIsHier;   //True if OldItem specifies hierarchical item
  140.     short         HasTopScrl;  //True if menu has top scroll arrow
  141.     short         HasBotScrl;  //True if menu has bottom scroll arrow
  142.     short         MustScrl;    //Which direction must menu scroll, if any?
  143.     short         ScrollAmt;   //Scrolling distance in pixels
  144.     RgnHandle     SavedClip;   //=> Clip region before DoChooseMsg is called
  145.     Rect          MenuClip;    //Menu's clip rectangle, if scroll icons present
  146.     RgnHandle     UpdateRgn;   //=> Menu update region when scrolled
  147.     Rect          UpdateRect;  //Menu update rectangle when scrolled
  148.     long          EnableFlags; //Menu's enable flags
  149.     Boolean       dontScroll;  /* True if scrolling shouldn’t take place */
  150.  
  151.     EnableFlags = (unsigned long) (**TheMenu).enableFlags;
  152.     if (EnableFlags & 1)
  153.         {
  154.         EnableFlags >>= (unsigned long) 1; //#N#
  155.         EnableFlags |= 0x80000000;
  156.         }
  157.     else
  158.         EnableFlags = (unsigned long) 0;
  159.     SavedClip = (RgnHandle) null;
  160.     NewRect = OldRect = *MenuRect;
  161.     NewRect.top = NewRect.bottom = OldRect.top = OldRect.bottom = sgTopMenuItem;
  162.     NewItem = 0;
  163.     OldItem = 0;
  164.     CurrItem = 1;
  165.     TotHeight = 0;
  166.     NewIsHier = OldIsHier = false;
  167.     dontScroll = *((short *) ((*sgMBSaveLoc) + 16)) == 1; //#O#
  168.     if (dontScroll)
  169.         *((short *) ((*sgMBSaveLoc) + 16)) = 0;
  170.     HLock ((Handle) TheMenu);
  171.     ItemString = (Str255 *) (**TheMenu).menuData;
  172.     ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString));
  173.     while ((*ItemString) [0] != '\0') //#A#
  174.         {
  175.         ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize (*ItemString));
  176.         ItemHeight = CalcItemHeight (*ItemString, ItemInfo);
  177.         if (NewItem == 0)
  178.             {
  179.             NewRect.bottom = NewRect.top + ItemHeight;
  180.             if (HitPt.v > NewRect.top && HitPt.v <= NewRect.bottom)
  181.                 {
  182.                 if (EnableFlags & 1) //#K#
  183.                     NewItem = CurrItem;
  184.                 else
  185.                     NewItem = -CurrItem;
  186.                 if (ItemInfo->kbdEquiv == (char) hMenuCmd)
  187.                     NewIsHier = true;
  188.                 }
  189.             else
  190.                 NewRect.top += ItemHeight;
  191.             }
  192.         if (OldItem == 0)
  193.             if (WhichItem == CurrItem)
  194.                 {
  195.                 OldRect.bottom = OldRect.top + ItemHeight;
  196.                 OldItem = WhichItem;
  197.                 if (ItemInfo->kbdEquiv == (char) hMenuCmd)
  198.                     OldIsHier = true;
  199.                 }
  200.             else
  201.                 OldRect.top += ItemHeight;
  202.         TotHeight += ItemHeight;
  203.         CurrItem += 1;
  204.         ItemString = (Str255 *) (ItemInfo + 1);
  205.         EnableFlags >>= 1;
  206.         }
  207.     HUnlock ((Handle) TheMenu);
  208.     if (HitPt.h < MenuRect->left || HitPt.h > MenuRect->right)
  209.         NewItem = 0;
  210.     MustScrl = noScroll;
  211.     HasTopScrl = HasBotScrl = false;
  212.     if (sgTopMenuItem != MenuRect->top) //#B#
  213.         {
  214.         HasTopScrl = true;
  215.         if (HitPt.v < MenuRect->top + scrlIconHeight)
  216.             {
  217.             NewItem = 0;
  218.             MustScrl = downScroll;
  219.             }
  220.         }
  221.     if (sgAtMenuBottom != MenuRect->bottom) //#C#
  222.         {
  223.         HasBotScrl = true;
  224.         if (HitPt.v > MenuRect->bottom - scrlIconHeight)
  225.             {
  226.             NewItem = 0;
  227.             MustScrl = upScroll;
  228.             }
  229.         }
  230.     if (HasBotScrl || HasTopScrl) //#D#
  231.         {
  232.         MenuClip = *MenuRect;
  233.         if (HasBotScrl)
  234.             MenuClip.bottom -= scrlIconHeight;
  235.         if (HasTopScrl)
  236.             MenuClip.top += scrlIconHeight;
  237.         SavedClip = NewRgn ();
  238.         GetClip (SavedClip);
  239.         ClipRect (&MenuClip);
  240.         }
  241.     if (NewItem == 0)
  242.         sgMenuDisable = 0L;
  243.     else
  244.         {
  245.          sgMenuDisable = (**TheMenu).menuID << (sizeof(long) / 2) | (NewItem > 0 ?
  246.                 NewItem : -NewItem); //#J# #K#
  247.         if (NewItem < 0)
  248.             NewItem = 0;
  249.         }
  250.     if (NewItem != 0 && NewIsHier) //#L#
  251.         {
  252.         sgHuhRect = NewRect;
  253.         *((Rect *) ((*sgMBSaveLoc) + 6)) = NewRect;
  254.         }
  255.     if (NewItem != OldItem)
  256.         {
  257.         if (OldItem != 0)
  258.             if (OldIsHier)
  259.                 RedrawItem (TheMenu, OldItem, &OldRect, false);
  260.             else
  261.                 InvertRect (&OldRect);
  262.         if (NewItem != 0)
  263.             if (OldIsHier) //#M#
  264.                 RedrawItem (TheMenu, NewItem, &NewRect, true);
  265.             else
  266.                 InvertRect (&NewRect);
  267.         }
  268.     if (!dontScroll && MustScrl && HitPt.h >= MenuRect->left && HitPt.h <=
  269.             MenuRect->right)
  270.         {
  271.         if (MustScrl == upScroll)
  272.             {
  273.             if (! HasTopScrl) //#E#
  274.                 {
  275.                 DrawScroll (MenuRect, topScroll);
  276.                 MenuClip.top += scrlIconHeight;
  277.                 }
  278.             ScrollAmt = MenuClip.bottom - HitPt.v;
  279.             if (ScrollAmt < MenuRect->bottom - sgAtMenuBottom)
  280.                 ScrollAmt = MenuRect->bottom - sgAtMenuBottom;
  281.             UpdateRgn = NewRgn ();
  282.             ScrollRect (&MenuClip, 0, ScrollAmt, UpdateRgn);
  283.             UpdateRect = (**UpdateRgn).rgnBBox;
  284.             DisposeRgn (UpdateRgn);
  285.             sgTopMenuItem += ScrollAmt; //#I#
  286.             sgAtMenuBottom += ScrollAmt;
  287.             if (sgAtMenuBottom == MenuRect->bottom) //#F#
  288.                 UpdateRect.bottom += scrlIconHeight;
  289.             if (SavedClip == (RgnHandle) null)
  290.                 {
  291.                 SavedClip = NewRgn ();
  292.                 GetClip (SavedClip);
  293.                 }
  294.             ClipRect (&UpdateRect);
  295.             EraseRect (&UpdateRect);
  296.             }
  297.         else
  298.             {
  299.             if (! HasBotScrl) //#E#
  300.                 {
  301.                 DrawScroll (MenuRect, botScroll);
  302.                 MenuClip.bottom -= scrlIconHeight;
  303.                 }
  304.             ScrollAmt = MenuClip.top - HitPt.v;
  305.             if (ScrollAmt > MenuRect->top - sgTopMenuItem)
  306.                 ScrollAmt = MenuRect->top - sgTopMenuItem;
  307.             UpdateRgn = NewRgn ();
  308.             ScrollRect (&MenuClip, 0, ScrollAmt, UpdateRgn);
  309.             UpdateRect = (**UpdateRgn).rgnBBox;
  310.             DisposeRgn (UpdateRgn);
  311.             sgTopMenuItem += ScrollAmt;
  312.             sgAtMenuBottom += ScrollAmt;
  313.             if (sgTopMenuItem == MenuRect->top) //#F#
  314.                 UpdateRect.top -= scrlIconHeight;
  315.             if (SavedClip == (RgnHandle) null)
  316.                 {
  317.                 SavedClip = NewRgn ();
  318.                 GetClip (SavedClip);
  319.                 }
  320.             ClipRect (&UpdateRect);
  321.             EraseRect (&UpdateRect);
  322.             }
  323.         SetRect (&ItemRect, MenuRect->left, sgTopMenuItem, MenuRect->right,
  324.                 sgTopMenuItem);
  325.         EnableFlags = (unsigned long) (**TheMenu).enableFlags;
  326.         if (EnableFlags & 1)
  327.             {
  328.             EnableFlags >>= (unsigned long) 1;
  329.             EnableFlags |= 0x80000000;
  330.             }
  331.         else
  332.             EnableFlags = (unsigned long) 0;
  333.         HLock ((Handle) TheMenu);
  334.         ItemString = (Str255 *) (**TheMenu).menuData;
  335.         ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString));
  336.         while ((*ItemString) [0] != '\0') //#G#
  337.             {
  338.             ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize
  339.                     (*ItemString));
  340.             ItemRect.bottom = ItemRect.top + CalcItemHeight (*ItemString,
  341.                     ItemInfo);
  342.             if (ItemRect.top < UpdateRect.bottom && ItemRect.bottom >
  343.                     UpdateRect.top)
  344.                 DrawItem (*ItemString, ItemInfo, &ItemRect, EnableFlags & 1);
  345.             EnableFlags >>= 1;
  346.             ItemRect.top = ItemRect.bottom;
  347.             ItemString = (Str255 *) (ItemInfo + 1);
  348.             }
  349.         HUnlock ((Handle) TheMenu);
  350.         }
  351.     if (SavedClip != (RgnHandle) null) //#H#
  352.         {
  353.         SetClip (SavedClip);
  354.         DisposeRgn (SavedClip);
  355.         }
  356.     return NewItem;
  357.     }
  358.  
  359.  
  360. #pragma segment Main
  361. /******************************************************************************\
  362. * RedrawItem - Redraw a menu item for hierarchical menus
  363. *
  364. * RedrawItem redraws a menu item that's been clobbered by a hierarchical menu.
  365. * The affected menu is specified by TheMenu.  The item number of the menu item
  366. * to redraw is given in ItemNum, while the item's rectangle is given in
  367. * ItemRect.  If the item is going to be selected, Select must be true so that
  368. * the item will be redrawn in inverse.  Otherwise Select must be false so that
  369. * the item will be redrawn normally.
  370. \******************************************************************************/
  371.  
  372. static void
  373. RedrawItem (MenuHandle TheMenu,short ItemNum,Rect* ItemRect,short Select)
  374. {
  375.     Str255       *ItemString; //-> Menu item's string
  376.     ItemInfoPtr  ItemInfo;    //-> Item info record
  377.     short        CurrItem;    //Item number of item being checked
  378.     TextStateRec TextState;   //Current text modes, etc.
  379.  
  380.     CurrItem = 0;
  381.     HLock ((Handle) TheMenu);
  382.     ItemString = (Str255 *) (**TheMenu).menuData;
  383.     ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString));
  384.     while (ItemNum != CurrItem && (*ItemString) [0] != '\0')
  385.         {
  386.         ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize
  387.                 (*ItemString));
  388.         CurrItem += 1;
  389.         if (ItemNum == CurrItem)
  390.             {
  391.             GetTextState (&TextState);
  392.             EraseRect (ItemRect);
  393.             DrawItem (*ItemString, ItemInfo, ItemRect, true);
  394.             if (Select)
  395.                 InvertRect (ItemRect);
  396.             SetTextState (&TextState);
  397.             }
  398.         else
  399.             ItemString = (Str255 *) (ItemInfo + 1);
  400.         }
  401.     HUnlock ((Handle) TheMenu);
  402.     }
  403.